﻿#include "pch.h"
#include "Server.h"

/**
 * 构造方法。
 */
Server::Server() {
    // 初始化网络
    WSADATA wsaData = { 0 };
    WSAStartup(MAKEWORD(2, 2), &wsaData);
}

/**
 * 析构方法。
 */
Server::~Server() {
    // 关闭套接字
    CSocket::Close();

    // 清空在线用户列表
    POSITION pos = mOnlineUsers.GetStartPosition();
    CString username;
    Client* client = NULL;
    while (pos) {
        mOnlineUsers.GetNextAssoc(pos, username, client);
        client->Close();
        delete client;
    }
    mOnlineUsers.RemoveAll();

    // 反初始化网络
    WSACleanup();
}

/**
 * 处理寻找其它用户请求。
 * @param request   请求
 * @param client    客户端
 */
void Server::HandleFindUsers(FindUsersRequest* request, Client* client) {
    // 创建结果
    FindUsersResult result;
    POSITION pos = mOnlineUsers.GetStartPosition();
    CString username;
    Client* rivalUser = NULL;
    int i = 0;
    while (pos) {
        mOnlineUsers.GetNextAssoc(pos, username, rivalUser);

        // 跳过自己的名字
        if (_tcscmp(request->mUsername, username) == 0) {
            continue;
        }
        _tcscpy_s(result.mUsernameList[i], sizeof(result.mUsernameList[i]) / sizeof(TCHAR), username);
        i++;
    }

    // 发送结果
    this->SendData(client, &result, sizeof(result));
}

/**
 * 处理登录请求。
 * @param request   请求
 * @param client    客户端
 */
void Server::HandleLogon(LogonRequest* request, Client* client) {
    // 如果已登录，则直接返回
    Client* onlineUser = NULL;
    BOOL isOnline = mOnlineUsers.Lookup(request->mUsername, onlineUser);
    if (isOnline) {
        LogonResult result(_T(""));
        this->SendData(client, &result, sizeof(result));
        return;
    }

    // 保存用户
    mOnlineUsers.SetAt(request->mUsername, client);

    // 创建结果
    LogonResult result(request->mUsername);

    // 发送结果
    this->SendData(client, &result, sizeof(result));
}

/**
 * 处理协议。
 * @param pro   协议
 */
void Server::HandleProtocol(GomokuProtocol* pro) {
    // 如果目标用户不在线则返回
    Client* rivalUser = NULL;
    BOOL isOnline = mOnlineUsers.Lookup(pro->mUsername, rivalUser);
    if (!isOnline) {
        return;
    }

    // 计算数据长度
    int dataSize = -1;
    switch (pro->mType) {
        case GomokuProtocol::INVITE_REQUEST:
            dataSize = sizeof(InviteRequest);
            break;
        case GomokuProtocol::INVITE_RESULT:
            dataSize = sizeof(InviteResult);
            break;
        case GomokuProtocol::PUT_PIECE:
            dataSize = sizeof(PutPiece);
            break;
        case GomokuProtocol::GAME_OVER:
            dataSize = sizeof(GameOver);
            break;
    }

    // 发送给目标用户
    this->SendData(rivalUser, pro, dataSize);
}

/**
 * 处理退出游戏请求。
 * @param request   请求
 * @param client    客户端
 */
void Server::HandleQuitGame(QuitGame* request, Client* client) {
    // 广播该用户的请求
    POSITION pos = mOnlineUsers.GetStartPosition();
    CString username;
    Client* rivalUser = NULL;
    while (pos) {
        mOnlineUsers.GetNextAssoc(pos, username, rivalUser);
        this->SendData(rivalUser, request, sizeof(QuitGame));
    }

    // 将该用户从在线列表移除
    mOnlineUsers.RemoveKey(request->mUsername);

    // 关闭客户端
    delete client;
}

/**
 * 客户端连接服务器事件的响应方法。
 * @param errorCode 错误码
 */
void Server::OnAccept(int errorCode) {
    Client* client = new Client(this);
    CSocket::Accept(*client);

    CSocket::OnAccept(errorCode);
}

/**
 * 处理客户端发送来的数据。
 * @param pro       协议
 * @param client    客户端
 */
void Server::OnGetMessageFromClient(GomokuProtocol* pro, Client* client) {
    switch (pro->mType) {
        case GomokuProtocol::FIND_USERS:
            this->HandleFindUsers((FindUsersRequest*)pro, client);
            break;
        case GomokuProtocol::LOGON:
            this->HandleLogon((LogonRequest*)pro, client);
            break;
        case GomokuProtocol::QUIT_GAME:
            this->HandleQuitGame((QuitGame*)pro, client);
            break;
        default:
            this->HandleProtocol(pro);
            break;
    }
}

/**
 * 发送数据。
 * @param client    客户端
 * @param pro       协议
 * @param dataSize  数据长度
 */
int Server::SendData(Client* client, GomokuProtocol* pro, int dataSize) {
    // 发送数据长度
    client->Send(&dataSize, sizeof(dataSize));

    // 发送数据
    return client->Send(pro, dataSize);
}

/**
 * 开启服务。
 * @return 成功则返回 TRUE，否则返回 FALSE
 */
BOOL Server::Start() {
    // 绑定 IP 地址和端口号
    BOOL result = CSocket::Create(GomokuProtocol::SERVER_PORT, SOCK_STREAM, GomokuProtocol::GetServerIP());
    if (!result) {
        return result;
    }

    // 侦听
    return CSocket::Listen();
}
